// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//
#pragma once

#include <cantor/type/type_kind.h>
#include <cantor/type/tree.h>
#include <cantor/type/enum_builder.h>
#include <turbo/log/logging.h>
#include <turbo/times/time.h>
#include <optional>
#include <type_traits>
#include <typeindex>
#include <turbo/container/flat_hash_map.h>
#include <turbo/container/span.h>
#include <turbo/utility/status.h>
#include <turbo/numeric/int128.h>

namespace cantor {
    namespace detail {
        turbo::uint128 compute_signature(std::string_view str);
    }

    template<pb::TypeKind KIND>
    class ScalarType;
    class TimestampType;
    class DurationType;
    class DateTimeType;
    class IntervalType;
    class EnumType;
    class ArrayType;
    class MapType;
    class HashMapType;
    class SetType;
    class HashSetType;
    class StructType;
    class TupleType;
    class UnionType;
    class FunctionType;
    class UdfType;
    class UnknownType;
    template<pb::TypeKind KIND>
    class DecimalType;
    class StringType;
    class BinaryType;
    class JsonType;
    class ProtoType;

    template<pb::TypeKind KIND>
    struct TypeTraits {
    };

    enum class PrimitiveType : uint32_t {
        DEFAULT = 0x0000,
        INTEGER = 0x0001,
        FLOAT = 0x0002,
        STRING = 0x0004,
        TIME = 0x0008,
        BOOLEAN = 0x0010,
        DECIMAL = 0x0020,
        ENUM = 0x0040,
        PRIMITIVE = 0x010000,
    };

    constexpr PrimitiveType operator|(PrimitiveType lhs, PrimitiveType rhs) {
        return static_cast<PrimitiveType>(static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs));
    }

    constexpr PrimitiveType operator&(PrimitiveType lhs, PrimitiveType rhs) {
        return static_cast<PrimitiveType>(static_cast<uint32_t>(lhs) & static_cast<uint32_t>(rhs));
    }

    constexpr PrimitiveType operator^(PrimitiveType lhs, PrimitiveType rhs) {
        return static_cast<PrimitiveType>(static_cast<uint32_t>(lhs) ^ static_cast<uint32_t>(rhs));
    }

    constexpr PrimitiveType operator~(PrimitiveType lhs) {
        return static_cast<PrimitiveType>(~static_cast<uint32_t>(lhs));
    }

    constexpr PrimitiveType &operator|=(PrimitiveType &lhs, PrimitiveType rhs) {
        lhs = static_cast<PrimitiveType>(static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs));
        return lhs;
    }

    constexpr PrimitiveType &operator&=(PrimitiveType &lhs, PrimitiveType rhs) {
        lhs = static_cast<PrimitiveType>(static_cast<uint32_t>(lhs) & static_cast<uint32_t>(rhs));
        return lhs;
    }

    constexpr PrimitiveType &operator^=(PrimitiveType &lhs, PrimitiveType rhs) {
        lhs = static_cast<PrimitiveType>(static_cast<uint32_t>(lhs) ^ static_cast<uint32_t>(rhs));
        return lhs;
    }

    constexpr bool is_integer(PrimitiveType type) {
        return static_cast<uint32_t>(type & PrimitiveType::INTEGER);
    }

    constexpr bool is_float(PrimitiveType type) {
        return static_cast<uint32_t>(type & PrimitiveType::FLOAT);
    }

    constexpr bool is_numeric(PrimitiveType type) {
        return static_cast<uint32_t>(type & PrimitiveType::INTEGER) || static_cast<uint32_t>(
                   type & PrimitiveType::FLOAT);
    }

    constexpr bool is_primitive(PrimitiveType type) {
        return static_cast<uint32_t>(type & PrimitiveType::PRIMITIVE);
    }

    turbo::Status check_unique(const std::vector<std::string> &values);

    //////////////////////////////////////////////////////////////////////////////////////
    /// Primitive Types
    //////////////////////////////////////////////////////////////////////////////////////
    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_BOOLEAN> {
        using impl_type = ScalarType<pb::TypeKind::TYPE_KIND_BOOLEAN>;
        using physical_type = bool;
        using deep_copied_type = physical_type;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_BOOLEAN;
        static constexpr PrimitiveType primitive_type = PrimitiveType::BOOLEAN | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "BOOLEAN";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_INT8> {
        using impl_type = ScalarType<pb::TypeKind::TYPE_KIND_INT8>;
        using physical_type = int8_t;
        using deep_copied_type = physical_type;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_INT8;
        static constexpr PrimitiveType primitive_type = PrimitiveType::INTEGER | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "INT8";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_INT16> {
        using impl_type = ScalarType<pb::TypeKind::TYPE_KIND_INT16>;
        using physical_type = int16_t;
        using deep_copied_type = physical_type;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_INT16;
        static constexpr PrimitiveType primitive_type = PrimitiveType::INTEGER | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "INT16";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_INT32> {
        using impl_type = ScalarType<pb::TypeKind::TYPE_KIND_INT32>;
        using physical_type = int32_t;
        using deep_copied_type = physical_type;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_INT32;
        static constexpr PrimitiveType primitive_type = PrimitiveType::INTEGER | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "INT32";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_INT64> {
        using impl_type = ScalarType<pb::TypeKind::TYPE_KIND_INT64>;
        using physical_type = int64_t;
        using deep_copied_type = physical_type;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_INT64;
        static constexpr PrimitiveType primitive_type = PrimitiveType::INTEGER | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "INT64";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_INT128> {
        using impl_type = ScalarType<pb::TypeKind::TYPE_KIND_INT128>;
        using physical_type = turbo::int128;
        using deep_copied_type = physical_type;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_INT128;
        static constexpr PrimitiveType primitive_type = PrimitiveType::INTEGER | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "INT128";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_UINT8> {
        using impl_type = ScalarType<pb::TypeKind::TYPE_KIND_UINT8>;
        using physical_type = uint8_t;
        using deep_copied_type = physical_type;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_UINT8;
        static constexpr PrimitiveType primitive_type = PrimitiveType::INTEGER | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "UINT8";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_UINT16> {
        using impl_type = ScalarType<pb::TypeKind::TYPE_KIND_UINT16>;
        using physical_type = uint16_t;
        using deep_copied_type = physical_type;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_UINT16;
        static constexpr PrimitiveType primitive_type = PrimitiveType::INTEGER | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "UINT16";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_UINT32> {
        using impl_type = ScalarType<pb::TypeKind::TYPE_KIND_UINT32>;
        using physical_type = uint32_t;
        using deep_copied_type = physical_type;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_UINT32;
        static constexpr PrimitiveType primitive_type = PrimitiveType::INTEGER | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "UINT32";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_UINT64> {
        using impl_type = ScalarType<pb::TypeKind::TYPE_KIND_UINT64>;
        using physical_type = uint64_t;
        using deep_copied_type = physical_type;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_UINT64;
        static constexpr PrimitiveType primitive_type = PrimitiveType::INTEGER | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "UINT64";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_UINT128> {
        using impl_type = ScalarType<pb::TypeKind::TYPE_KIND_UINT128>;
        using physical_type = turbo::uint128;
        using deep_copied_type = physical_type;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_UINT128;
        static constexpr PrimitiveType primitive_type = PrimitiveType::INTEGER | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "UINT128";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_FP8> {
        using impl_type = ScalarType<pb::TypeKind::TYPE_KIND_FP8>;
        using physical_type = uint8_t;
        using deep_copied_type = physical_type;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_FP8;
        static constexpr PrimitiveType primitive_type = PrimitiveType::FLOAT | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "FP8";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_FP16> {
        using impl_type = ScalarType<pb::TypeKind::TYPE_KIND_FP16>;
        using physical_type = uint16_t;
        using deep_copied_type = physical_type;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_FP16;
        static constexpr PrimitiveType primitive_type = PrimitiveType::FLOAT | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "FP16";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_FP32> {
        using impl_type = ScalarType<pb::TypeKind::TYPE_KIND_FP32>;
        using physical_type = float;
        using deep_copied_type = physical_type;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_FP32;
        static constexpr PrimitiveType primitive_type = PrimitiveType::FLOAT | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "FP32";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_FP64> {
        using impl_type = ScalarType<pb::TypeKind::TYPE_KIND_FP64>;
        using physical_type = double;
        using deep_copied_type = physical_type;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_FP64;
        static constexpr PrimitiveType primitive_type = PrimitiveType::FLOAT | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "FP64";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_STRING> {
        using impl_type = StringType;
        using physical_type = std::string_view;
        using deep_copied_type = std::string;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_STRING;
        static constexpr PrimitiveType primitive_type = PrimitiveType::STRING | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = false;
        static constexpr bool is_fixed_serialize_width = false;
        static constexpr const char *name = "STRING";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_BINARY> {
        using impl_type = BinaryType;
        using physical_type = std::string_view;
        using deep_copied_type = std::string;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_BINARY;
        static constexpr PrimitiveType primitive_type = PrimitiveType::STRING | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = false;
        static constexpr bool is_fixed_serialize_width = false;
        static constexpr const char *name = "BINARY";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_TIMESTAMP> {
        using impl_type = TimestampType;
        using physical_type = turbo::Time;
        using deep_copied_type = int64_t;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_TIMESTAMP;
        static constexpr PrimitiveType primitive_type = PrimitiveType::TIME | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "TIMESTAMP";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_DURATION> {
        using impl_type = DurationType;
        using physical_type = turbo::Duration;
        using deep_copied_type = int64_t;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_DURATION;
        static constexpr PrimitiveType primitive_type = PrimitiveType::TIME | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "DURATION";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_DATETIME> {
        using impl_type = DateTimeType;
        using physical_type = turbo::CivilSecond;
        using deep_copied_type = int64_t;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_DATETIME;
        static constexpr PrimitiveType primitive_type = PrimitiveType::TIME | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "DATETIME";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_INTERVAL> {
        using impl_type = IntervalType;
        using physical_type = turbo::CivilSecond;
        using deep_copied_type = int64_t;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_INTERVAL;
        static constexpr PrimitiveType primitive_type = PrimitiveType::TIME | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "INTERVAL";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_DECIMAL64> {
        using impl_type = DecimalType<pb::TypeKind::TYPE_KIND_DECIMAL64>;
        using physical_type = uint64_t;
        using deep_copied_type = uint64_t;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_DECIMAL64;
        static constexpr PrimitiveType primitive_type = PrimitiveType::DECIMAL | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "DECIMAL64";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_DECIMAL128> {
        using impl_type = DecimalType<pb::TypeKind::TYPE_KIND_DECIMAL128>;
        using physical_type = turbo::uint128;
        using deep_copied_type = turbo::uint128;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_DECIMAL128;
        static constexpr PrimitiveType primitive_type = PrimitiveType::DECIMAL | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "DECIMAL128";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_ENUM> {
        using impl_type = EnumType;
        using physical_type = uint32_t;
        using deep_copied_type = uint32_t;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_ENUM;
        static constexpr PrimitiveType primitive_type = PrimitiveType::ENUM | PrimitiveType::PRIMITIVE;
        static constexpr bool is_fixed_width = true;
        static constexpr bool is_fixed_serialize_width = true;
        static constexpr const char *name = "ENUM";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_ARRAY> {
        using impl_type = ArrayType;
        using physical_type = turbo::span<uint8_t>;
        using deep_copied_type = turbo::span<uint8_t>;
        static constexpr uint32_t min_sub_types = 1;
        static constexpr uint32_t max_sub_types = 1;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_ARRAY;
        static constexpr PrimitiveType primitive_type = PrimitiveType::DEFAULT;
        static constexpr bool is_fixed_width = false;
        static constexpr bool is_fixed_serialize_width = false;
        static constexpr const char *name = "ARRAY";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_MAP> {
        using impl_type = MapType;
        using physical_type = std::map<int32_t, int32_t>;
        using deep_copied_type = turbo::span<uint8_t>;
        static constexpr uint32_t min_sub_types = 2;
        static constexpr uint32_t max_sub_types = 2;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_MAP;
        static constexpr PrimitiveType primitive_type = PrimitiveType::DEFAULT;
        static constexpr bool is_fixed_width = false;
        static constexpr bool is_fixed_serialize_width = false;
        static constexpr const char *name = "MAP";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_SET> {
        using impl_type = SetType;
        using physical_type = std::set<int32_t>;
        using deep_copied_type = turbo::span<uint8_t>;
        static constexpr uint32_t min_sub_types = 1;
        static constexpr uint32_t max_sub_types = 1;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_SET;
        static constexpr PrimitiveType primitive_type = PrimitiveType::DEFAULT;
        static constexpr bool is_fixed_width = false;
        static constexpr bool is_fixed_serialize_width = false;
        static constexpr const char *name = "SET";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_STRUCT> {
        using impl_type = StructType;
        using physical_type = std::shared_ptr<void>;
        using deep_copied_type = turbo::span<uint8_t>;
        static constexpr uint32_t min_sub_types = 1;
        static constexpr uint32_t max_sub_types = std::numeric_limits<uint32_t>::max();
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_STRUCT;
        static constexpr PrimitiveType primitive_type = PrimitiveType::DEFAULT;
        static constexpr bool is_fixed_width = false;
        static constexpr bool is_fixed_serialize_width = false;
        static constexpr const char *name = "STRUCT";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_TUPLE> {
        using impl_type = TupleType;
        using physical_type = std::shared_ptr<void>;
        using deep_copied_type = turbo::span<uint8_t>;
        static constexpr uint32_t min_sub_types = 1;
        static constexpr uint32_t max_sub_types = std::numeric_limits<uint32_t>::max();
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_TUPLE;
        static constexpr PrimitiveType primitive_type = PrimitiveType::DEFAULT;
        static constexpr bool is_fixed_width = false;
        static constexpr bool is_fixed_serialize_width = false;
        static constexpr const char *name = "TUPLE";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_UNION> {
        using impl_type = UnionType;
        using physical_type = std::shared_ptr<void>;
        using deep_copied_type = turbo::span<uint8_t>;
        static constexpr uint32_t min_sub_types = 1;
        static constexpr uint32_t max_sub_types = std::numeric_limits<uint32_t>::max();
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_UNION;
        static constexpr PrimitiveType primitive_type = PrimitiveType::DEFAULT;
        static constexpr bool is_fixed_width = false;
        static constexpr bool is_fixed_serialize_width = false;
        static constexpr const char *name = "UNION";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_UDF> {
        using impl_type = UdfType;
        using physical_type = std::shared_ptr<void>;
        using deep_copied_type = std::shared_ptr<void>;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_UDF;
        static constexpr PrimitiveType primitive_type = PrimitiveType::DEFAULT;
        static constexpr bool is_fixed_width = false;
        static constexpr bool is_fixed_serialize_width = false;
        static constexpr const char *name = "UDF";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_JSON> {
        using impl_type = JsonType;
        using physical_type = std::shared_ptr<void>;
        using deep_copied_type = std::string;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_JSON;
        static constexpr PrimitiveType primitive_type = PrimitiveType::DEFAULT;
        static constexpr bool is_fixed_width = false;
        static constexpr bool is_fixed_serialize_width = false;
        static constexpr const char *name = "JSON";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_PROTO> {
        using impl_type = ProtoType;
        using physical_type = std::shared_ptr<void>;
        using deep_copied_type = std::string;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_PROTO;
        static constexpr PrimitiveType primitive_type = PrimitiveType::DEFAULT;
        static constexpr bool is_fixed_width = false;
        static constexpr bool is_fixed_serialize_width = false;
        static constexpr const char *name = "PROTO";
    };

    template<>
    struct TypeTraits<pb::TypeKind::TYPE_KIND_NONE> {
        using impl_type = void;
        using physical_type = void;
        using deep_copied_type = void;
        static constexpr uint32_t min_sub_types = 0;
        static constexpr uint32_t max_sub_types = 0;
        static constexpr pb::TypeKind typeKind = pb::TypeKind::TYPE_KIND_NONE;
        static constexpr PrimitiveType primitive_type = PrimitiveType::DEFAULT;
        static constexpr bool is_fixed_width = false;
        static constexpr bool is_fixed_serialize_width = false;
        static constexpr const char *name = "NONE";
    };

    template<pb::TypeKind KIND>
    struct TypeFactory;

    class Type;
    using TypePtr = std::shared_ptr<const Type>;

    /// Abstract class hierarchy. Instances of these classes carry full
    /// information about types, including for example field names.
    /// Can be instantiated by factory methods, like INTEGER()
    /// or MAP(INTEGER(), BIGINT()).
    /// Instances of these classes form a tree, and are immutable.
    /// For example, MAP<INTEGER, ARRAY<BIGINT>> will form a tree like:
    ///
    ///             MapType
    ///           /         \
    ///   IntegerType    ArrayType
    ///                     |
    ///                   BigintType
    ///

    /// using to pass parameters helper
    struct ConstantParameter {
        bool is_primitive_type{false};
        bool is_integer_type{false};
        bool is_float_type{false};
        bool is_numeric_type{false};
        bool is_orderable{false};
        bool is_comparable{false};
        const char *name{nullptr};
        turbo::uint128 signature;
        const char *kind_name{nullptr};
        std::string full_name;
    };

    class Type : public Tree<const TypePtr> {
    public:
        explicit Type(pb::TypeKind kind, ConstantParameter param)
            : is_primitive_type(param.is_primitive_type),
              is_integer_type(param.is_integer_type),
              is_float_type(param.is_float_type),
              is_numeric_type(param.is_numeric_type),
              is_orderable(param.is_orderable),
              is_comparable(param.is_comparable),
              name{param.name},
              signature{param.signature},
              kind_name{param.kind_name},
              full_name(param.full_name),
              kind_{kind} {
            KCHECK(name) << "name is nullptr";
            KCHECK(kind_name) << "kind_name is nullptr";
            KCHECK(signature != turbo::uint128{}) << "name is nullptr";
        }

        pb::TypeKind kind() const {
            return kind_;
        }

        virtual ~Type() = default;

        /// This convenience method makes pattern matching easier. Rather than having
        /// to know the implementation type up front, just use as<pb::TypeKind::TYPE_KIND_MAP> (for
        /// example) to dynamically cast to the appropriate type.
        template<pb::TypeKind KIND>
        const typename TypeTraits<KIND>::impl_type &as() const {
            return dynamic_cast<const typename TypeTraits<KIND>::impl_type &>(*this);
        }

        const bool is_primitive_type{false};

        const bool is_integer_type{false};

        const bool is_float_type{false};

        const bool is_numeric_type{false};

        /// Returns true if less than relationship is defined for the values of this
        /// type, i.e. a <= b returns true or false. For example, scalar types are
        /// usually orderable, arrays and structs are orderable if their nested types
        /// are, while map types are not orderable.
        const bool is_orderable{false};

        /// Returns true if equality relationship is defined for the values of this
        /// type, i.e. a == b is defined and returns true, false or null. For example,
        /// scalar types are usually comparable and complex types are comparable if
        /// their nested types are.
        const bool is_comparable{false};


        /// Returns unique logical type name. It can be
        /// different from the physical type name returned by 'kind_name'.
        const char *const name{nullptr};

        const turbo::uint128 signature;

        /// Returns physical type name. Multiple logical types may share the same
        /// physical type backing and therefore return the same physical type name.
        /// The logical type name returned by 'name' must be unique though.
        const char *const kind_name{nullptr};

        const std::string full_name;

        //virtual std::string to_string() const = 0;


        bool equals(const Type &other) const {
            return this->signature == other.signature;
        }

        /// For Complex types (Row, Array, Map, Opaque): types are strongly matched.
        /// For primitive types: same as equivalent.
        bool operator==(const Type &other) const {
            return this->signature == other.signature;
        }

        inline bool operator!=(const Type &other) const {
            return !(*this == other);
        }

        // todo(jeff): avoid expensive virtual function calls for these
        // simple functions
        virtual size_t runtime_size() const {
            return 0;
        }

        virtual size_t serialize_size() const {
            return 0;
        }

        virtual bool is_fixed_width() const = 0;

        virtual bool is_fixed_serialize_width() const = 0;

        /// Recursive kind hashing (uses only pb::TypeKind).
        size_t hash_kind() const;

        /// Recursive kind match (uses only pb::TypeKind).
        bool kind_equals(const std::shared_ptr<const Type> &other) const;

        template<pb::TypeKind KIND, typename... CHILDREN>
        static std::shared_ptr<const typename TypeTraits<KIND>::impl_type> create(
            CHILDREN... children) {
            return TypeFactory<KIND>::create(std::forward<CHILDREN...>(children)...);
        }

        /// BOOLEAN
        const TypeTraits<pb::TypeKind::TYPE_KIND_BOOLEAN>::impl_type &as_boolean() const {
            return this->as<pb::TypeKind::TYPE_KIND_BOOLEAN>();
        }

        bool is_boolean() const { return this->kind() == pb::TypeKind::TYPE_KIND_BOOLEAN; }

        /// INT8
        const TypeTraits<pb::TypeKind::TYPE_KIND_INT8>::impl_type &as_int8() const {
            return this->as<pb::TypeKind::TYPE_KIND_INT8>();
        }

        bool is_int8() const { return this->kind() == pb::TypeKind::TYPE_KIND_INT8; }

        /// INT16
        const TypeTraits<pb::TypeKind::TYPE_KIND_INT16>::impl_type &as_int16() const {
            return this->as<pb::TypeKind::TYPE_KIND_INT16>();
        }

        bool is_int16() const { return this->kind() == pb::TypeKind::TYPE_KIND_INT16; }

        /// INT32
        const TypeTraits<pb::TypeKind::TYPE_KIND_INT32>::impl_type &as_int32() const {
            return this->as<pb::TypeKind::TYPE_KIND_INT32>();
        }

        bool is_int32() const { return this->kind() == pb::TypeKind::TYPE_KIND_INT32; }

        /// INT64
        const TypeTraits<pb::TypeKind::TYPE_KIND_INT64>::impl_type &as_int64() const {
            return this->as<pb::TypeKind::TYPE_KIND_INT64>();
        }

        bool is_int64() const { return this->kind() == pb::TypeKind::TYPE_KIND_INT64; }

        /// INT128
        const TypeTraits<pb::TypeKind::TYPE_KIND_INT128>::impl_type &as_int128() const {
            return this->as<pb::TypeKind::TYPE_KIND_INT128>();
        }

        bool is_int128() const { return this->kind() == pb::TypeKind::TYPE_KIND_INT128; }

        /// UINT8
        const TypeTraits<pb::TypeKind::TYPE_KIND_UINT8>::impl_type &as_uint8() const {
            return this->as<pb::TypeKind::TYPE_KIND_UINT8>();
        }

        bool is_uint8() const { return this->kind() == pb::TypeKind::TYPE_KIND_UINT8; }

        /// UINT16
        const TypeTraits<pb::TypeKind::TYPE_KIND_UINT16>::impl_type &as_uint16() const {
            return this->as<pb::TypeKind::TYPE_KIND_UINT16>();
        }

        bool is_uint16() const { return this->kind() == pb::TypeKind::TYPE_KIND_UINT16; }

        /// UINT32
        const TypeTraits<pb::TypeKind::TYPE_KIND_UINT32>::impl_type &as_uint32() const {
            return this->as<pb::TypeKind::TYPE_KIND_UINT32>();
        }

        bool is_uint32() const { return this->kind() == pb::TypeKind::TYPE_KIND_UINT32; }

        /// UINT64
        const TypeTraits<pb::TypeKind::TYPE_KIND_UINT64>::impl_type &as_uint64() const {
            return this->as<pb::TypeKind::TYPE_KIND_UINT64>();
        }

        bool is_uint64() const { return this->kind() == pb::TypeKind::TYPE_KIND_UINT64; }

        /// UINT128
        const TypeTraits<pb::TypeKind::TYPE_KIND_UINT128>::impl_type &as_uint128() const {
            return this->as<pb::TypeKind::TYPE_KIND_UINT128>();
        }

        bool is_uint128() const { return this->kind() == pb::TypeKind::TYPE_KIND_UINT128; }

        /// FP8
        const TypeTraits<pb::TypeKind::TYPE_KIND_FP8>::impl_type &as_fp8() const {
            return this->as<pb::TypeKind::TYPE_KIND_FP8>();
        }

        bool is_fp8() const { return this->kind() == pb::TypeKind::TYPE_KIND_FP8; }

        /// FP16
        const TypeTraits<pb::TypeKind::TYPE_KIND_FP16>::impl_type &as_fp16() const {
            return this->as<pb::TypeKind::TYPE_KIND_FP16>();
        }

        bool is_fp16() const { return this->kind() == pb::TypeKind::TYPE_KIND_FP16; }

        /// FP32
        const TypeTraits<pb::TypeKind::TYPE_KIND_FP32>::impl_type &as_fp32() const {
            return this->as<pb::TypeKind::TYPE_KIND_FP32>();
        }

        bool is_fp32() const { return this->kind() == pb::TypeKind::TYPE_KIND_FP32; }

        /// FP64
        const TypeTraits<pb::TypeKind::TYPE_KIND_FP64>::impl_type &as_fp64() const {
            return this->as<pb::TypeKind::TYPE_KIND_FP64>();
        }

        bool is_fp64() const { return this->kind() == pb::TypeKind::TYPE_KIND_FP64; }

        /// STRING
        const TypeTraits<pb::TypeKind::TYPE_KIND_STRING>::impl_type &as_string() const {
            return this->as<pb::TypeKind::TYPE_KIND_STRING>();
        }

        bool is_string() const { return this->kind() == pb::TypeKind::TYPE_KIND_STRING; }

        /// BINARY
        const TypeTraits<pb::TypeKind::TYPE_KIND_BINARY>::impl_type &as_binary() const {
            return this->as<pb::TypeKind::TYPE_KIND_BINARY>();
        }

        bool is_binary() const { return this->kind() == pb::TypeKind::TYPE_KIND_BINARY; }

        /// TIMESTAMP
        const TypeTraits<pb::TypeKind::TYPE_KIND_TIMESTAMP>::impl_type &as_timestamp() const {
            return this->as<pb::TypeKind::TYPE_KIND_TIMESTAMP>();
        }

        bool is_timestamp() const { return this->kind() == pb::TypeKind::TYPE_KIND_TIMESTAMP; }

        /// DURATION
        const TypeTraits<pb::TypeKind::TYPE_KIND_DURATION>::impl_type &as_duration() const {
            return this->as<pb::TypeKind::TYPE_KIND_DURATION>();
        }

        bool is_duration() const { return this->kind() == pb::TypeKind::TYPE_KIND_DURATION; }

        /// DATETIME
        const TypeTraits<pb::TypeKind::TYPE_KIND_DATETIME>::impl_type &as_datetime() const {
            return this->as<pb::TypeKind::TYPE_KIND_DATETIME>();
        }

        bool is_datetime() const { return this->kind() == pb::TypeKind::TYPE_KIND_DATETIME; }

        /// INTERVAL
        const TypeTraits<pb::TypeKind::TYPE_KIND_INTERVAL>::impl_type &as_interval() const {
            return this->as<pb::TypeKind::TYPE_KIND_INTERVAL>();
        }

        bool is_interval() const { return this->kind() == pb::TypeKind::TYPE_KIND_INTERVAL; }

        /// DECIMAL64
        const TypeTraits<pb::TypeKind::TYPE_KIND_DECIMAL64>::impl_type &as_decimal64() const {
            return this->as<pb::TypeKind::TYPE_KIND_DECIMAL64>();
        }

        bool is_decimal64() const { return this->kind() == pb::TypeKind::TYPE_KIND_DECIMAL64; }

        /// DECIMAL128
        const TypeTraits<pb::TypeKind::TYPE_KIND_DECIMAL128>::impl_type &as_decimal128() const {
            return this->as<pb::TypeKind::TYPE_KIND_DECIMAL128>();
        }

        bool is_decimal128() const { return this->kind() == pb::TypeKind::TYPE_KIND_DECIMAL128; }

        /// ENUM
        const TypeTraits<pb::TypeKind::TYPE_KIND_ENUM>::impl_type &as_enum() const {
            return this->as<pb::TypeKind::TYPE_KIND_ENUM>();
        }

        bool is_enum() const { return this->kind() == pb::TypeKind::TYPE_KIND_ENUM; }

        /// ARRAY
        const TypeTraits<pb::TypeKind::TYPE_KIND_ARRAY>::impl_type &as_array() const {
            return this->as<pb::TypeKind::TYPE_KIND_ARRAY>();
        }

        bool is_array() const { return this->kind() == pb::TypeKind::TYPE_KIND_ARRAY; }

        /// MAP
        const TypeTraits<pb::TypeKind::TYPE_KIND_MAP>::impl_type &as_map() const {
            return this->as<pb::TypeKind::TYPE_KIND_MAP>();
        }

        bool is_map() const { return this->kind() == pb::TypeKind::TYPE_KIND_MAP; }

        /// SET
        const TypeTraits<pb::TypeKind::TYPE_KIND_SET>::impl_type &as_set() const {
            return this->as<pb::TypeKind::TYPE_KIND_SET>();
        }

        bool is_set() const { return this->kind() == pb::TypeKind::TYPE_KIND_SET; }


        /// STRUCT
        const TypeTraits<pb::TypeKind::TYPE_KIND_STRUCT>::impl_type &as_struct() const {
            return this->as<pb::TypeKind::TYPE_KIND_STRUCT>();
        }

        bool is_struct() const { return this->kind() == pb::TypeKind::TYPE_KIND_STRUCT; }

        /// TUPLE
        const TypeTraits<pb::TypeKind::TYPE_KIND_TUPLE>::impl_type &as_tuple() const {
            return this->as<pb::TypeKind::TYPE_KIND_TUPLE>();
        }

        bool is_tuple() const { return this->kind() == pb::TypeKind::TYPE_KIND_TUPLE; }

        /// UNION
        const TypeTraits<pb::TypeKind::TYPE_KIND_UNION>::impl_type &as_union() const {
            return this->as<pb::TypeKind::TYPE_KIND_UNION>();
        }

        bool is_union() const { return this->kind() == pb::TypeKind::TYPE_KIND_UNION; }

        /// UDF
        const TypeTraits<pb::TypeKind::TYPE_KIND_UDF>::impl_type &as_udf() const {
            return this->as<pb::TypeKind::TYPE_KIND_UDF>();
        }

        bool is_udf() const { return this->kind() == pb::TypeKind::TYPE_KIND_UDF; }

    private:
        const pb::TypeKind kind_;

    public:
        static const char *getClassName() noexcept {
            return "Type";
        }
    };

    template<pb::TypeKind KIND>
    class TypeBase : public Type {
    public:
        using physical_type = typename TypeTraits<KIND>::physical_type;

        static constexpr pb::TypeKind TYPE = KIND;

        explicit TypeBase(bool is_orderable, bool is_comparable, turbo::uint128 signature, std::string full_name)
            : Type{
                KIND, {
                    is_primitive(TypeTraits<KIND>::primitive_type),
                    is_integer(TypeTraits<KIND>::primitive_type),
                    is_float(TypeTraits<KIND>::primitive_type),
                    is_numeric(TypeTraits<KIND>::primitive_type),
                    is_orderable,
                    is_comparable,
                    TypeTraits<KIND>::name,
                    signature,
                    TypeTraits<KIND>::name,
                    std::move(full_name)
                }
            } {
        }

        explicit TypeBase(bool is_orderable, bool is_comparable, const char *name, turbo::uint128 signature,
                          std::string full_name)
            : Type{
                KIND, {
                    is_primitive(TypeTraits<KIND>::primitive_type),
                    is_integer(TypeTraits<KIND>::primitive_type),
                    is_float(TypeTraits<KIND>::primitive_type),
                    is_numeric(TypeTraits<KIND>::primitive_type),
                    is_orderable,
                    is_comparable,
                    name,
                    signature,
                    TypeTraits<KIND>::name,
                    std::move(full_name)
                }
            } {
        }

        explicit TypeBase(ConstantParameter param)
            : Type{KIND, param} {
        }

        bool is_fixed_width() const override {
            return TypeTraits<KIND>::is_fixed_width;
        }

        bool is_fixed_serialize_width() const override {
            return TypeTraits<KIND>::is_fixed_serialize_width;
        }

        size_t runtime_size() const override {
            return sizeof(typename TypeTraits<KIND>::physical_type);
        }

        size_t serialize_size() const override {
            return sizeof(typename TypeTraits<KIND>::deep_copied_type);
        }
    };

    template<pb::TypeKind KIND>
    class ScalarType : public TypeBase<KIND> {
    public:
        explicit ScalarType(const char *name, turbo::uint128 signature, std::string full_name)
            : TypeBase<KIND>(true, true, name, signature, std::move(full_name)) {
        }

        explicit ScalarType(bool isorder, bool iscompare, const char *name, turbo::uint128 signature,
                            std::string full_name)
            : TypeBase<KIND>(isorder, iscompare, name, signature, std::move(full_name)) {
        }

        uint32_t size() const override {
            return 0;
        }

        const std::shared_ptr<const Type> &child_at(uint32_t) const override {
            KCHECK(false) << "scalar type has no children";
            return nullptr;
        }
    };

    template<pb::TypeKind KIND>
    class NumericType : public ScalarType<KIND> {
    public:
        NumericType() : ScalarType<KIND>(TypeTraits<KIND>::name, init_signature(),
                                         std::string(TypeTraits<KIND>::name)) {
            static_assert(KIND == pb::TypeKind::TYPE_KIND_INT8 ||
                          KIND == pb::TypeKind::TYPE_KIND_INT16 ||
                          KIND == pb::TypeKind::TYPE_KIND_INT32 ||
                          KIND == pb::TypeKind::TYPE_KIND_INT64 ||
                          KIND == pb::TypeKind::TYPE_KIND_INT128 ||
                          KIND == pb::TypeKind::TYPE_KIND_UINT8 ||
                          KIND == pb::TypeKind::TYPE_KIND_UINT16 ||
                          KIND == pb::TypeKind::TYPE_KIND_UINT32 ||
                          KIND == pb::TypeKind::TYPE_KIND_UINT64 ||
                          KIND == pb::TypeKind::TYPE_KIND_UINT128 ||
                          KIND == pb::TypeKind::TYPE_KIND_FP8 ||
                          KIND == pb::TypeKind::TYPE_KIND_FP16 ||
                          KIND == pb::TypeKind::TYPE_KIND_FP32 ||
                          KIND == pb::TypeKind::TYPE_KIND_FP64 ||
                          KIND == pb::TypeKind::TYPE_KIND_BOOLEAN,
                          "must be numeric type"
            );
        }

        ~NumericType() override {
        }

        static turbo::uint128 init_signature() {
            auto str = turbo::str_cat("L0:", TypeTraits<KIND>::name, "{}");
            return detail::compute_signature(str);
        }

        static const std::shared_ptr<const NumericType<KIND>> create();
    };

    template<pb::TypeKind KIND>
    const std::shared_ptr<const NumericType<KIND>> NumericType<KIND>::create() {
        static const auto instance = std::make_shared<const NumericType<KIND>>();
        return instance;
    }

    template<typename Sink>
    void turbo_stringify(Sink &sink, pb::TimePrecision p) {
        switch (p) {
            case pb::TimePrecision::TSP_NANO:
                sink.Append("nano");
                return;
            case pb::TimePrecision::TSP_MICRO:
                sink.Append("micro");
                return;
            case pb::TimePrecision::TSP_MILLI:
                sink.Append("milli");
                return;
            case pb::TimePrecision::TSP_SECONDS:
                sink.Append("seconds");
                return;
            default:
                sink.Append("unknown");
                return;
        }
    }

    inline constexpr bool is_valid_timestamp_precision(pb::TimePrecision p) {
        switch (p) {
            case pb::TimePrecision::TSP_NANO:
            case pb::TimePrecision::TSP_MICRO:
            case pb::TimePrecision::TSP_MILLI:
            case pb::TimePrecision::TSP_SECONDS:
                return true;
            default:
                return false;
        }
    }

    class TimestampType : public ScalarType<pb::TypeKind::TYPE_KIND_TIMESTAMP> {
    public:
        static constexpr const char *TYPE_NAME = "TIMESTAMP";

        TimestampType(const pb::TimePrecision precision = pb::TimePrecision::TSP_NANO,
                      const std::string &tz = "local")
            : ScalarType<pb::TypeKind::TYPE_KIND_TIMESTAMP>(TYPE_NAME, init_signature(precision, tz),
                                                        turbo::str_format(
                                                            "TIMESTAMP(%v, %s)", precision, tz)),
              _precision(precision), _time_zone(tz) {
            KCHECK(is_valid_timestamp_precision(_precision)) << "Timestamp type is invalid";
        }

        static turbo::uint128 init_signature(pb::TimePrecision p, std::string_view tz) {
            auto str = turbo::str_cat("L0:", TYPE_NAME, "{ precision:", TimePrecision_Name(p), ",timezone:", tz, "}");
            return detail::compute_signature(str);
        }

        inline pb::TimePrecision precision() const {
            return _precision;
        }

        inline const std::string &timezone() const {
            return _time_zone;
        }

    private:
        const pb::TimePrecision _precision{pb::TimePrecision::TSP_NANO};
        const std::string _time_zone;
    };

    class DurationType : public ScalarType<pb::TypeKind::TYPE_KIND_DURATION> {
    public:
        static constexpr const char *TYPE_NAME = "DURATION";

        DurationType(const pb::TimePrecision precision = pb::TimePrecision::TSP_NANO)
            : ScalarType<pb::TypeKind::TYPE_KIND_DURATION>(TYPE_NAME, init_signature(precision),
                                                       turbo::str_format("DURATION(%v)", precision)),
              _precision(precision) {
            KCHECK(is_valid_timestamp_precision(_precision)) << "Timestamp type is invalid";
        }

        static turbo::uint128 init_signature(pb::TimePrecision p) {
            auto str = turbo::str_cat("L0:", TYPE_NAME, "{ precision:", TimePrecision_Name(p), "}");
            return detail::compute_signature(str);
        }

        inline pb::TimePrecision precision() const {
            return _precision;
        }

    private:
        const pb::TimePrecision _precision{pb::TimePrecision::TSP_NANO};
    };

    inline constexpr bool is_valid_date_precision(pb::DateTimePrecision p) {
        switch (p) {
            case pb::DateTimePrecision::DTP_NANO:
            case pb::DateTimePrecision::DTP_MICRO:
            case pb::DateTimePrecision::DTP_MILLI:
            case pb::DateTimePrecision::DTP_SECOND:
            case pb::DateTimePrecision::DTP_MINUTE:
            case pb::DateTimePrecision::DTP_HOUR:
            case pb::DateTimePrecision::DTP_DAY:
            default:
                return false;
        }
    }

    class DateTimeType : public ScalarType<pb::TypeKind::TYPE_KIND_DATETIME> {
    public:
        static constexpr const char *TYPE_NAME = "DATETIME";

        DateTimeType(const pb::DateTimePrecision precision = pb::DateTimePrecision::DTP_NANO,
                     const std::string &tz = "local")
            : ScalarType<pb::TypeKind::TYPE_KIND_DATETIME>(TYPE_NAME, init_signature(precision, tz),
                                                       turbo::str_format("DATETIME(%v, %s)", precision, tz)),
              _precision(precision),
              _time_zone(tz) {
            KCHECK(is_valid_date_precision(_precision)) << "DateTime type is invalid";
        }

        static turbo::uint128 init_signature(pb::DateTimePrecision p, std::string_view tz) {
            auto str = turbo::str_cat("L0:", TYPE_NAME, "{ precision:", DateTimePrecision_Name(p), ",timezone:", tz,
                                      "}");
            return detail::compute_signature(str);
        }

        inline pb::DateTimePrecision precision() const {
            return _precision;
        }

        inline const std::string &timezone() const {
            return _time_zone;
        }

    private:
        const pb::DateTimePrecision _precision{pb::DateTimePrecision::DTP_NANO};
        const std::string _time_zone;
    };

    class IntervalType : public ScalarType<pb::TypeKind::TYPE_KIND_INTERVAL> {
    public:
        static constexpr const char *TYPE_NAME = "DURATION";

        IntervalType(const pb::DateTimePrecision precision = pb::DateTimePrecision::DTP_NANO)
            : ScalarType<pb::TypeKind::TYPE_KIND_INTERVAL>(TYPE_NAME, init_signature(precision),
                                                       turbo::str_format("DURATION(%v)", precision)),
              _precision(precision) {
            KCHECK(is_valid_date_precision(_precision)) << "Interval type is invalid";
        }

        static turbo::uint128 init_signature(pb::DateTimePrecision p) {
            auto str = turbo::str_cat("L0:", TYPE_NAME, "{ precision:", DateTimePrecision_Name(p), "}");
            return detail::compute_signature(str);
        }

        inline pb::DateTimePrecision precision() const {
            return _precision;
        }

    private:
        const pb::DateTimePrecision _precision{pb::DateTimePrecision::DTP_NANO};
    };

    typedef turbo::Result<std::string> (*resolve_enum_handler)(const std::vector<std::string> &names);

    class EnumType : public ScalarType<pb::TypeKind::TYPE_KIND_ENUM> {
    public:
        static constexpr const char *TYPE_NAME = "ENUM";

        EnumType(EnumData data)
            : ScalarType<pb::TypeKind::TYPE_KIND_ENUM>(TYPE_NAME, init_signature(data),
                                                   data.to_string()), _data(std::move(data)) {
        }

        static turbo::uint128 init_signature(EnumData p) {
            auto str = turbo::str_cat("L0:", TYPE_NAME, "{ EnumData:", p.to_string(), "}");
            return detail::compute_signature(str);
        }

        [[nodiscard]] uint32_t max_value() const {
            return _data.max_value;
        }

        const std::string &uri() const {
            return _data.uri;
        }

        size_t enum_size() const {
            return _data.name_to_value.size();
        }

        size_t value_size() const {
            return _data.value_to_name.size();
        }

        turbo::Result<uint32_t> construct_enum(const std::string &em) const;

        turbo::Status construct_enum(uint32_t ev) const;

        turbo::Result<std::string> resolve_enum(uint32_t ev) const;

        turbo::Result<std::string> resolve_enum(uint32_t ev, resolve_enum_handler handler) const;

        bool has_alias() const {
            return _data.has_alias;
        }

    private:
        const EnumData _data;
    };


    /// This class represents the fixed-point numbers.
    /// The parameter "precision" represents the number of digits the
    /// Decimal Type can support and "scale" represents the number of digits to
    /// the right of the decimal point.
    template<pb::TypeKind KIND>
    class DecimalType : public ScalarType<KIND> {
    public:
        static_assert(KIND == pb::TypeKind::TYPE_KIND_DECIMAL64 || KIND == pb::TypeKind::TYPE_KIND_DECIMAL128);
        static constexpr uint8_t kMaxPrecision = KIND == pb::TypeKind::TYPE_KIND_DECIMAL64 ? 18 : 38;
        static constexpr uint8_t kMinPrecision = KIND == pb::TypeKind::TYPE_KIND_DECIMAL64 ? 1 : 19;
        static constexpr uint8_t kIs64Bit = KIND == pb::TypeKind::TYPE_KIND_DECIMAL64;

        static constexpr const char *TYPE_NAME64 = "DECIMAL64";
        static constexpr const char *TYPE_NAME128 = "DECIMAL128";

        static turbo::Status check_parameters(const uint8_t precision, const uint8_t scale) {
            if (scale > precision) {
                return turbo::invalid_argument_error("Scale of decimal type must not exceed its precision [", scale,
                                                     " vs ", precision, "]");
            }
            if (precision > kMaxPrecision) {
                return turbo::invalid_argument_error("Precision of decimal type must not exceed ", kMaxPrecision);
            }
            if (precision < kMinPrecision) {
                return turbo::invalid_argument_error("Precision of decimal type must be at least ", kMinPrecision);
            }
            return turbo::OkStatus();
        }

        inline uint8_t precision() const {
            return _precision;
        }

        inline uint8_t scale() const {
            return _scale;
        }

        static std::string make_full_name(const uint8_t precision, const uint8_t scale) {
            if constexpr (kIs64Bit)
                return turbo::str_format("DECIMAL64(%d, %d)", precision, scale);
            return turbo::str_format("DECIMAL128(%d, %d)", precision, scale);
        }

        static turbo::uint128 init_signature(const uint8_t precision, const uint8_t scale) {
            auto str = turbo::str_cat("L0:", (kIs64Bit ? TYPE_NAME64 : TYPE_NAME128), "{precision:", precision,
                                      "scale:", scale, "}");
            return detail::compute_signature(str);
        }

        DecimalType(const uint8_t precision, const uint8_t scale)
            : ScalarType<KIND>(kIs64Bit ? TYPE_NAME64 : TYPE_NAME128, init_signature(precision, scale),
                               make_full_name(precision, scale)),
              _precision(precision), _scale(scale) {
        }

    private:
        int64_t _precision{0};
        int64_t _scale{0};
    };

    /// ArrayType
    /// std::vector<int64_t> dims, when value <=0, means unlimited
    /// when the ArrayType child type is primitive and
    class ArrayType : public TypeBase<pb::TypeKind::TYPE_KIND_ARRAY> {
    public:
        static constexpr const char *TYPE_NAME = "ARRAY";

        explicit ArrayType(TypePtr child, std::vector<int64_t> dims)
            : TypeBase<pb::TypeKind::TYPE_KIND_ARRAY>(
                  child->is_orderable,
                  child->is_comparable, init_signature(child, dims), make_full_name(child, dims)),
              child_(std::move(child)),
              _dimensions(std::move(dims)),
              _is_fixed_width(
                  check_fixed(_dimensions) && child_->
                  is_fixed_width()) {
        }

        static turbo::uint128 init_signature(const TypePtr &child, const std::vector<int64_t> &dims);

        const TypePtr &element_type() const {
            return child_;
        }

        uint32_t size() const override {
            return 1;
        }

        std::vector<TypePtr> children() const {
            return {child_};
        }

        std::vector<std::string> names() const {
            return {"element"};
        }

        bool is_fixed_width() const override {
            return _is_fixed_width;
        }

        bool is_fixed_serialize_width() const override {
            return _is_fixed_width;
        }

        const std::shared_ptr<const Type> &child_at(uint32_t idx) const override;

        const char *name_of(uint32_t idx) const {
            KCHECK_EQ(idx, 0) << "Array type should have only one child";
            return "element";
        }

        static std::string make_full_name(const TypePtr &child, const std::vector<int64_t> &dims) {
            return "ARRAY<" + child->full_name + ">" + make_bound(dims);
        }

        const std::vector<int64_t> &dims() const {
            return _dimensions;
        }

        static std::string make_bound(const std::vector<int64_t> &dims);

    protected:
        static bool check_fixed(const std::vector<int64_t> &bounds);

        TypePtr child_;
        const std::vector<int64_t> _dimensions;
        const bool _is_fixed_width;
    };

    class MapType : public TypeBase<pb::TypeKind::TYPE_KIND_MAP> {
    public:
        static constexpr const char *TYPE_NAME = "MAP";

        MapType(TypePtr keyType, TypePtr valueType): TypeBase<pb::TypeKind::TYPE_KIND_MAP>(
                                                         keyType->is_orderable,
                                                         keyType->is_comparable && valueType->is_comparable,
                                                         init_signature(keyType, valueType),
                                                         make_full_name(keyType, valueType)),
                                                     keyType_{std::move(keyType)},
                                                     valueType_{std::move(valueType)} {
        }

        static turbo::uint128 init_signature(const TypePtr &keyType, const TypePtr &valueType);

        const TypePtr &key_type() const {
            return keyType_;
        }

        const TypePtr &value_type() const {
            return valueType_;
        }

        uint32_t size() const override {
            return 2;
        }

        std::vector<TypePtr> children() const {
            return {keyType_, valueType_};
        }

        std::vector<std::string> names() const {
            return {"key", "value"};
        }

        static std::string make_full_name(const TypePtr &keyType, const TypePtr &valueType) {
            return "MAP<" + keyType->full_name + "," + valueType->full_name + ">";
        }

        const TypePtr &child_at(uint32_t idx) const override;

        const char *name_of(uint32_t idx) const;

    private:
        TypePtr keyType_;
        TypePtr valueType_;
    };

    class SetType : public TypeBase<pb::TypeKind::TYPE_KIND_SET> {
    public:
        static constexpr const char *TYPE_NAME = "MAP";

        SetType(TypePtr keyType) : TypeBase<pb::TypeKind::TYPE_KIND_SET>(
                                       keyType->is_orderable,
                                       keyType->is_comparable, init_signature(keyType), make_full_name(keyType)),
                                   keyType_{std::move(keyType)} {
        }

        static turbo::uint128 init_signature(const TypePtr &keyType);

        explicit SetType(
            std::vector<std::string> && /*names*/,
            std::vector<TypePtr> &&types)
            : SetType(std::move(types[0])) {
        }

        const TypePtr &keyType() const {
            return keyType_;
        }

        uint32_t size() const override {
            return 2;
        }

        std::vector<TypePtr> children() const {
            return {keyType_};
        }

        std::vector<std::string> names() const {
            return {"key"};
        }

        static std::string make_full_name(const TypePtr &keyType) {
            return "SET<" + keyType->full_name + ">";
        }

        const TypePtr &child_at(uint32_t idx) const override;

        const char *name_of(uint32_t idx) const;

    private:
        TypePtr keyType_;
    };

    class StructType : public TypeBase<pb::TypeKind::TYPE_KIND_STRUCT> {
    public:
        StructType(
            std::vector<std::string> &&names,
            std::vector<std::shared_ptr<const Type> > &&types);

        ~StructType() override = default;

        static turbo::uint128 init_signature(const std::vector<std::string> &names,
                                             const std::vector<std::shared_ptr<const Type> > &types);

        uint32_t size() const final {
            return children_.size();
        }

        const TypePtr &child_at(uint32_t idx) const final {
            KCHECK_LT(idx, children_.size());
            return children_[idx];
        }

        const std::vector<std::shared_ptr<const Type> > &children() const {
            return children_;
        }

        const std::shared_ptr<const Type> &findChild(std::string_view name) const;

        bool contains_child(std::string_view name) const;

        uint32_t get_child_idx(std::string_view name) const;

        std::optional<uint32_t> get_child_idx_if_exists(std::string_view name) const;

        const std::string &name_of(uint32_t idx) const {
            KCHECK_LT(idx, names_.size());
            return names_[idx];
        }

        static std::string make_full_name(const std::vector<std::string> &names,
                                          const std::vector<std::shared_ptr<const Type> > &types);

        /// Print child names and types separated by 'delimiter'.
        static void print_children(std::stringstream &ss, const std::vector<std::string> &names,
                                   const std::vector<std::shared_ptr<const Type> > &types,
                                   std::string_view delimiter = ",");

        const std::vector<std::string> &names() const {
            return names_;
        }

    private:
        const std::vector<std::string> names_;
        const std::vector<std::shared_ptr<const Type> > children_;
    };

    class TupleType : public TypeBase<pb::TypeKind::TYPE_KIND_TUPLE> {
    public:
        TupleType(std::vector<std::shared_ptr<const Type> > &&types);

        ~TupleType() override;

        static turbo::uint128 init_signature(const std::vector<std::shared_ptr<const Type> > &types);

        uint32_t size() const final {
            return children_.size();
        }

        const TypePtr &child_at(uint32_t idx) const final {
            KCHECK_LT(idx, children_.size());
            return children_[idx];
        }

        const std::vector<std::shared_ptr<const Type> > &children() const {
            return children_;
        }

        static std::string make_full_name(const std::vector<std::shared_ptr<const Type> > &types);

        /// Print child names and types separated by 'delimiter'.
        static void print_children(std::stringstream &ss, const std::vector<std::shared_ptr<const Type> > &types,
                                   std::string_view delimiter = ",");

    private:
        const std::vector<std::shared_ptr<const Type> > children_;
    };

    class UnionType : public TypeBase<pb::TypeKind::TYPE_KIND_UNION> {
    public:
        UnionType(
            std::vector<std::string> &&names,
            std::vector<std::shared_ptr<const Type> > &&types);

        ~UnionType() override;

        static turbo::uint128 init_signature(const std::vector<std::string> &names,
                                             const std::vector<std::shared_ptr<const Type> > &types);

        uint32_t size() const final {
            return children_.size();
        }

        const TypePtr &child_at(uint32_t idx) const final {
            KCHECK_LT(idx, children_.size());
            return children_[idx];
        }

        const std::vector<std::shared_ptr<const Type> > &children() const {
            return children_;
        }

        const std::shared_ptr<const Type> &findChild(std::string_view name) const;

        bool contains_child(std::string_view name) const;

        uint32_t get_child_idx(std::string_view name) const;

        std::optional<uint32_t> get_child_idx_if_exists(std::string_view name) const;

        const std::string &name_of(uint32_t idx) const {
            KCHECK_LT(idx, names_.size());
            return names_[idx];
        }

        static std::string make_full_name(const std::vector<std::string> &names,
                                          const std::vector<std::shared_ptr<const Type> > &types);

        /// Print child names and types separated by 'delimiter'.
        static void print_children(std::stringstream &ss, const std::vector<std::string> &names,
                                   const std::vector<std::shared_ptr<const Type> > &types,
                                   std::string_view delimiter = ",");

        const std::vector<std::string> &names() const {
            return names_;
        }

    private:
        const std::vector<std::string> names_;
        const std::vector<std::shared_ptr<const Type> > children_;
    };

    class UdfType : public TypeBase<pb::TypeKind::TYPE_KIND_UDF> {
    public:
        explicit UdfType(const std::string &uri, const TypePtr &ptr = nullptr) : TypeBase<pb::TypeKind::TYPE_KIND_UDF>(
                false, false, init_signature(uri), "UDF"),
            _uri(uri), _type(ptr) {
        }

        static turbo::uint128 init_signature(const std::string &uri) {
            auto str = turbo::str_cat("L0:UDF{uri:", uri, "}");
            return detail::compute_signature(str);
        }

        uint32_t size() const override {
            return 0;
        }

        const std::shared_ptr<const Type> &child_at(uint32_t) const override {
            KCHECK(false) << "UdfType type has no children";
            return nullptr;
        }

        const std::string &uri() const {
            return _uri;
        }

        /// In special cases specific OpaqueTypes might want to serialize additional
        /// metadata. In those cases we need to deserialize it back. Since
        /// UdfType::create<T>() returns canonical type for T without metadata,
        /// we allow to create new instance here or return nullptr if the same one
        /// can be used. Note that it's about deserialization of type itself,
        /// DeserializeFunc above is about deserializing instances of the type. It's
        /// implemented as a virtual member instead of a standalone registry just
        /// for convenience.

        TURBO_NOINLINE static std::shared_ptr<const UdfType> create(const std::string &uri) {
            /// static vars in templates are dangerous across DSOs, but it's just a
            /// performance optimization. Comparison looks at type_index anyway.
            static const auto instance =
                    std::make_shared<const UdfType>(uri);
            return instance;
        }

        const TypePtr type_ptr() const {
            return _type;
        }

        bool has_real_type() const {
            return _type != nullptr;
        }

    private:
        const std::string _uri;
        TypePtr _type;
    };

    class StringType : public ScalarType<pb::TypeKind::TYPE_KIND_STRING> {
    public:
        static constexpr const char *TYPE_NAME = "STRING";

        StringType(bool fixed_length, uint32_t length = std::numeric_limits<uint32_t>::max())
            : ScalarType<pb::TypeKind::TYPE_KIND_STRING>(TYPE_NAME, init_signature(fixed_length, length), TYPE_NAME),
              _is_fixed(fixed_length), _max_length(length) {
        }

        static turbo::uint128 init_signature(bool fixed_length, uint32_t length) {
            auto str = turbo::str_cat("L0:", TYPE_NAME, "{fixed_length:", fixed_length, "length:", length, "]");
            return detail::compute_signature(str);
        }

        uint32_t max_length() const {
            return _max_length;
        }

        bool is_fixed() const {
            return _is_fixed;
        }

    private:
        const bool _is_fixed{false};
        const uint32_t _max_length{0};
    };

    class BinaryType : public ScalarType<pb::TypeKind::TYPE_KIND_BINARY> {
    public:
        static constexpr const char *TYPE_NAME = "BINARY";

        BinaryType(bool fixed_length, uint32_t length = std::numeric_limits<uint32_t>::max())
            : ScalarType<pb::TypeKind::TYPE_KIND_BINARY>(TYPE_NAME, init_signature(fixed_length, length), TYPE_NAME),
              _is_fixed(fixed_length), _max_length(length) {
        }

        static turbo::uint128 init_signature(bool fixed_length, uint32_t length) {
            auto str = turbo::str_cat("L0:", TYPE_NAME, "{fixed_length:", fixed_length, "length:", length, ")");
            return detail::compute_signature(str);
        }

        uint32_t max_length() const {
            return _max_length;
        }

        bool is_fixed() const {
            return _is_fixed;
        }

    private:
        const bool _is_fixed{false};
        const uint32_t _max_length{0};
    };


    class JsonType : public ScalarType<pb::TypeKind::TYPE_KIND_JSON> {
    public:
        static constexpr const char *TYPE_NAME = "JSON";

        JsonType(std::string uri)
            : ScalarType<pb::TypeKind::TYPE_KIND_JSON>(TYPE_NAME, init_signature(uri), make_full_name(uri)),
              _schema(std::move(uri)) {
        }

        static turbo::uint128 init_signature(const std::string &uri) {
            auto str = turbo::str_cat("L0:JSON{uri:", uri, "}");
            return detail::compute_signature(str);
        }

        const std::string &uri() const {
            return _schema;
        }

        static std::string make_full_name(const std::string &uri) {
            return turbo::str_format("JSON(%s)", uri);
        }

    private:
        const std::string _schema;
    };

    class ProtoType : public ScalarType<pb::TypeKind::TYPE_KIND_PROTO> {
    public:
        static constexpr const char *TYPE_NAME = "PROTO";

        ProtoType(std::string uri)
            : ScalarType<pb::TypeKind::TYPE_KIND_PROTO>(false, false, TYPE_NAME, init_signature(uri), make_full_name(uri)),
              _uri(std::move(uri)) {
        }

        static turbo::uint128 init_signature(const std::string &uri) {
            auto str = turbo::str_cat("L0:PROTO{uri:", uri, "}");
            return detail::compute_signature(str);
        }

        const std::string &uri() const {
            return _uri;
        }

        static std::string make_full_name(const std::string &uri) {
            return turbo::str_format("PROTO(%s)", uri);
        }

    private:
        const std::string _uri;
    };

    using Int8Type = ScalarType<pb::TypeKind::TYPE_KIND_INT8>;
    using Int16Type = ScalarType<pb::TypeKind::TYPE_KIND_INT16>;
    using Int32Type = ScalarType<pb::TypeKind::TYPE_KIND_INT32>;
    using Int64Type = ScalarType<pb::TypeKind::TYPE_KIND_INT64>;
    using Int128Type = ScalarType<pb::TypeKind::TYPE_KIND_INT128>;
    using UInt8Type = ScalarType<pb::TypeKind::TYPE_KIND_UINT8>;
    using UInt16Type = ScalarType<pb::TypeKind::TYPE_KIND_UINT16>;
    using UInt32Type = ScalarType<pb::TypeKind::TYPE_KIND_UINT32>;
    using UInt64Type = ScalarType<pb::TypeKind::TYPE_KIND_UINT64>;
    using UInt128Type = ScalarType<pb::TypeKind::TYPE_KIND_UINT128>;
    using BooleanType = ScalarType<pb::TypeKind::TYPE_KIND_BOOLEAN>;
    using Fp8Type = ScalarType<pb::TypeKind::TYPE_KIND_FP8>;
    using Fp16Type = ScalarType<pb::TypeKind::TYPE_KIND_FP16>;
    using Fp32Type = ScalarType<pb::TypeKind::TYPE_KIND_FP32>;
    using Fp64Type = ScalarType<pb::TypeKind::TYPE_KIND_FP64>;

    template<pb::TypeKind KIND>
    struct TypeFactory {
        static std::shared_ptr<const typename TypeTraits<KIND>::impl_type> create() {
            return TypeTraits<KIND>::impl_type::create();
        }
    };

    ///////////////////////////////////////////////////////////////////////////////////
    /// CREATORS
    ///////////////////////////////////////////////////////////////////////////////////
    /// BOOL
    std::shared_ptr<const NumericType<pb::TypeKind::TYPE_KIND_BOOLEAN>> BOOLEAN();

    /// INT*
    std::shared_ptr<const NumericType<pb::TypeKind::TYPE_KIND_INT8>> INT8();

    std::shared_ptr<const NumericType<pb::TypeKind::TYPE_KIND_INT16>> INT16();

    std::shared_ptr<const NumericType<pb::TypeKind::TYPE_KIND_INT32>> INT32();

    std::shared_ptr<const NumericType<pb::TypeKind::TYPE_KIND_INT64>> INT64();

    std::shared_ptr<const NumericType<pb::TypeKind::TYPE_KIND_INT128>> INT128();

    /// UINT*
    std::shared_ptr<const NumericType<pb::TypeKind::TYPE_KIND_UINT8>> UINT8();

    std::shared_ptr<const NumericType<pb::TypeKind::TYPE_KIND_UINT16>> UINT16();

    std::shared_ptr<const NumericType<pb::TypeKind::TYPE_KIND_UINT32>> UINT32();

    std::shared_ptr<const NumericType<pb::TypeKind::TYPE_KIND_UINT64>> UINT64();

    std::shared_ptr<const NumericType<pb::TypeKind::TYPE_KIND_UINT128>> UINT128();

    /// FP*
    std::shared_ptr<const NumericType<pb::TypeKind::TYPE_KIND_FP8>> FP8();

    std::shared_ptr<const NumericType<pb::TypeKind::TYPE_KIND_FP16>> FP16();

    std::shared_ptr<const NumericType<pb::TypeKind::TYPE_KIND_FP32>> FP32();

    std::shared_ptr<const NumericType<pb::TypeKind::TYPE_KIND_FP64>> FP64();

    /// STRING
    std::shared_ptr<const StringType> STRING(uint32_t max = std::numeric_limits<uint32_t>::max());

    std::shared_ptr<const StringType> FIXED_STRING(uint32_t max);

    /// BINARY
    std::shared_ptr<const BinaryType> BINARY(uint32_t max = std::numeric_limits<uint32_t>::max());

    std::shared_ptr<const BinaryType> FIXED_BINARY(uint32_t max);

    /// TIMESTAMP
    turbo::Result<std::shared_ptr<const TimestampType> > TIMESTAMP(pb::TimePrecision precision,
                                                                   const std::string &tz = "local");

    std::shared_ptr<const TimestampType> NANO_TIMESTAMP(const std::string &tz = "local");

    std::shared_ptr<const TimestampType> MICRO_TIMESTAMP(const std::string &tz = "local");

    std::shared_ptr<const TimestampType> MILLI_TIMESTAMP(const std::string &tz = "local");

    std::shared_ptr<const TimestampType> SECOND_TIMESTAMP(const std::string &tz = "local");

    /// DURATION
    turbo::Result<std::shared_ptr<const DurationType> > DURATION(pb::TimePrecision precision);

    std::shared_ptr<const DurationType> NANO_DURATION();

    std::shared_ptr<const DurationType> MICRO_DURATION();

    std::shared_ptr<const DurationType> MILLI_DURATION();

    std::shared_ptr<const DurationType> SECOND_DURATION();

    /// DATETIME
    turbo::Result<std::shared_ptr<const DateTimeType> > DATETIME(pb::DateTimePrecision precision,
                                                                 const std::string &tz = "local");

    std::shared_ptr<const DateTimeType> NANO_DATETIME(const std::string &tz = "local");

    std::shared_ptr<const DateTimeType> MICRO_DATETIME(const std::string &tz = "local");

    std::shared_ptr<const DateTimeType> MILLI_DATETIME(const std::string &tz = "local");

    std::shared_ptr<const DateTimeType> SECOND_DATETIME(const std::string &tz = "local");

    std::shared_ptr<const DateTimeType> MINUTE_DATETIME(const std::string &tz = "local");

    std::shared_ptr<const DateTimeType> HOUR_DATETIME(const std::string &tz = "local");

    std::shared_ptr<const DateTimeType> DAY_DATETIME(const std::string &tz = "local");

    /// INTERVAL
    turbo::Result<std::shared_ptr<const IntervalType> > INTERVAL(pb::DateTimePrecision precision);

    std::shared_ptr<const IntervalType> NANO_INTERVAL();

    std::shared_ptr<const IntervalType> MICRO_INTERVAL();

    std::shared_ptr<const IntervalType> MILLI_INTERVAL();

    std::shared_ptr<const IntervalType> SECOND_INTERVAL();

    std::shared_ptr<const IntervalType> MINUTE_INTERVAL();

    std::shared_ptr<const IntervalType> HOUR_INTERVAL();

    std::shared_ptr<const IntervalType> DAY_INTERVAL();

    /// DECIMAL
    turbo::Result<std::shared_ptr<const DecimalType<pb::TypeKind::TYPE_KIND_DECIMAL64>> > DECIMAL64(
        const uint8_t precision, const uint8_t scale);

    turbo::Result<std::shared_ptr<const DecimalType<pb::TypeKind::TYPE_KIND_DECIMAL128>> > DECIMAL128(
        const uint8_t precision, const uint8_t scale);

    /// ENUM
    turbo::Result<std::shared_ptr<const EnumType> > ENUM(EnumData data);

    /// ARRAY
    turbo::Result<std::shared_ptr<const ArrayType> > ARRAY(TypePtr ptr, std::vector<int64_t> dims);

    turbo::Result<std::shared_ptr<const ArrayType> > ARRAY_UNLIMITED(TypePtr ptr, uint32_t dim);

    turbo::Result<std::shared_ptr<const ArrayType> > ARRAY_WITH_BOUND(TypePtr ptr, int64_t dim);

    turbo::Result<std::shared_ptr<const ArrayType> > ARRAY_WITH_BOUND(TypePtr ptr, int64_t dim0, int64_t dim1);

    turbo::Result<std::shared_ptr<const ArrayType> > ARRAY_WITH_BOUND(TypePtr ptr, int64_t dim0, int64_t dim1,
                                                                      int64_t dim2);

    /// MAP
    turbo::Result<std::shared_ptr<const MapType> > MAP(TypePtr key, TypePtr value);

    turbo::Result<std::shared_ptr<const HashMapType> > HASHMAP(TypePtr key, TypePtr value);

    /// SET
    turbo::Result<std::shared_ptr<const SetType> > SET(TypePtr key);

    turbo::Result<std::shared_ptr<const HashSetType> > HASHSET(TypePtr key);

    /// STRUCT
    turbo::Result<std::shared_ptr<const StructType> > STRUCT(std::vector<std::string> &&names,
                                                             std::vector<std::shared_ptr<const Type> > &&types);

    /// TUPLE
    turbo::Result<std::shared_ptr<const TupleType> > TUPLE(std::vector<std::shared_ptr<const Type> > &&types);

    /// UNION
    turbo::Result<std::shared_ptr<const UnionType> > UNION(std::vector<std::string> &&names,
                                                           std::vector<std::shared_ptr<const Type> > &&types);

    /// UDF
    turbo::Result<std::shared_ptr<const UdfType> > UDF(const std::string &uri);

    turbo::Result<std::shared_ptr<const UdfType> > UDF(const std::string &uri, const TypePtr &type);

    /// JSON
    turbo::Result<std::shared_ptr<const JsonType> > JSON(const std::string &schema);

    /// PROTO
    turbo::Result<std::shared_ptr<const ProtoType> > PROTO(const std::string &uri);
} // namespace cantor
